//////////////////////////////////////////////////
// glCamera.cpp
//
// Author: Tarik Belabbas et Adrien Hoffer
//////////////////////////////////////////////////

#define GL_GLEXT_PROTOTYPES

#include <GL/glu.h>
#include <cmath>
#include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
#include <glm/gtc/quaternion.hpp>

#include "glCamera.h"

//------------------------------------------------------------------------------
glCamera::glCamera()
{
	m_camPosition.x		= 2.0F;
	m_camPosition.y		= 0.0F;
	m_camPosition.z		= 2.0F;

	m_camLookPoint.x	= 0.0F;
	m_camLookPoint.y	= 0.0F;
	m_camLookPoint.z	= 0.0F;

	m_camUpVector.x		= 0.0F;
	m_camUpVector.y		= 1.0F;
	m_camUpVector.z		= 0.0F;

	m_camFlags			= glECameraMovementFlag_NONE;

	//glm::gtc::quaternion::rotate()
	//m_camOrientation.tquat()
}

//------------------------------------------------------------------------------
glCamera::~glCamera()
{

}

//------------------------------------------------------------------------------
void glCamera::update()
{
	gluLookAt(	m_camPosition.x, m_camPosition.y, m_camPosition.z,	// Position
			m_camLookPoint.x, m_camLookPoint.y, m_camLookPoint.z,	// Where we are looking
			m_camUpVector.x, m_camUpVector.y, m_camUpVector.z );	// Up vector

	/*glRotatef( m_xRotation, 1.0F, 0.0F, 0.0F );
	glRotatef( m_yRotation, 0.0F, 1.0F, 0.0F );
	glRotatef( m_zRotation, 0.0F, 0.0F, 1.0F );*/
}

/*//------------------------------------------------------------------------------
void glCamera::changeZoom( GLint _delta, GLfloat _viewportRatio )
{
	float numDegrees	= _delta / 8;
	float numSteps		= numDegrees / 15;

	m_zoomFactor -= VIEWPORT_ZOOM_STEP / numSteps;

	if( m_zoomFactor < VIEWPORT_ZOOM_MIN )
		m_zoomFactor = VIEWPORT_ZOOM_MIN;

	if( m_zoomFactor > VIEWPORT_ZOOM_MAX )
		m_zoomFactor = VIEWPORT_ZOOM_MAX;

	setPerspective( 90.0F * m_zoomFactor, _viewportRatio, CAM_ZNEAR, CAM_ZFAR );
}*/

//------------------------------------------------------------------------------
void glCamera::resizeViewport( GLfloat _viewportRatio )
{
	GLfloat zoomFactor = 1.0F;

	setPerspective( CAM_FOV * zoomFactor, _viewportRatio, CAM_ZNEAR, CAM_ZFAR );
}

//------------------------------------------------------------------------------
void glCamera::updateMove()
{
	glm::vec3 camLookVector = m_camLookPoint - m_camPosition;
	camLookVector = glm::normalize( camLookVector );

	GLfloat moveStep = CAM_MOVE_STEP;
	if ( m_camFlags & glECameraMovementFlag_SPEEDX100 )
	{
		moveStep *= 100;
	}
	if ( m_camFlags & glECameraMovementFlag_SPEEDX10 )
	{
		moveStep *= 10;
	}

	if ( m_camFlags & glECameraMovementFlag_FORWARD )
	{
		m_camPosition	+= moveStep * camLookVector;
		m_camLookPoint	+= moveStep * camLookVector;
	}
	else if ( m_camFlags & glECameraMovementFlag_BACKWARD )
	{
		m_camPosition	-= moveStep * camLookVector;
		m_camLookPoint	-= moveStep * camLookVector;
	}
	else if ( ( m_camFlags & glECameraMovementFlag_LEFT )
			|| ( m_camFlags & glECameraMovementFlag_RIGHT ) )
	{
		glm::vec3 camStrafeVector = glm::cross( camLookVector, m_camUpVector );
		camStrafeVector = glm::normalize( camStrafeVector );

		if ( m_camFlags & glECameraMovementFlag_LEFT )
		{
			m_camPosition	-= moveStep * camStrafeVector;
			m_camLookPoint	-= moveStep * camStrafeVector;
		}
		else
		{
			m_camPosition	+= moveStep * camStrafeVector;
			m_camLookPoint	+= moveStep * camStrafeVector;
		}
	}
}

//------------------------------------------------------------------------------
void glCamera::rotateX( float _angleX )
{
	glm::mat4 rotationMatrix;
	rotationMatrix = glm::rotate( rotationMatrix, _angleX, m_camUpVector );

	glm::vec3 camLookVector = m_camLookPoint - m_camPosition;

	glm::vec4 vector = glm::vec4( camLookVector, 1.0F );
	vector			= vector * rotationMatrix;
	camLookVector	= glm::vec3( vector );

	m_camLookPoint	= m_camPosition + camLookVector;
}

//------------------------------------------------------------------------------
void glCamera::rotateY( float _angleY )
{
	glm::vec3 camLookVector		= m_camLookPoint - m_camPosition;
	glm::vec3 camStrafeVector	= glm::cross( camLookVector, m_camUpVector );

	// Rotate camera look point
	glm::mat4 rotationMatrix;
	rotationMatrix = glm::rotate( rotationMatrix, _angleY, camStrafeVector );

	glm::vec4 vector = glm::vec4( camLookVector, 1.0F );
	vector			= vector * rotationMatrix;
	camLookVector	= glm::vec3( vector );

	m_camLookPoint	= m_camPosition + camLookVector;

	// Rotate camera up vector
	vector			= glm::vec4( m_camUpVector, 1.0F );
	vector			= vector * rotationMatrix;
	m_camUpVector	= glm::vec3( vector );
}

//------------------------------------------------------------------------------
void glCamera::moveCameraTo( float _posX, float _posY, float _posZ )
{
	m_camPosition = glm::vec3( _posX, _posY, _posZ );
}

//------------------------------------------------------------------------------
glm::mat4 glCamera::getModelViewMatrix() const
{
	glm::mat4 modelViewMatrix;
	glGetFloatv( GL_MODELVIEW_MATRIX, &( modelViewMatrix[ 0 ][ 0 ] ) );

	return modelViewMatrix;
}

//------------------------------------------------------------------------------
/*void glCamera::normalizeAngle( float& _angle )
{
	while ( _angle < 0.0F )
		_angle += 360.0F;

	while ( _angle > 360.0F )
		_angle -= 360.0F;
}*/

//------------------------------------------------------------------------------
void glCamera::setPerspective( float _yFieldOfView, double _aspect,
		double _zNear, double _zFar )
{
	// !! gluPerspective is deprecated !!
	// The following code is a fancy bit of math that is equivalent to calling:
	// gluPerspective( fieldOfView/2.0F, width/height , 0.1f, 255.0F )
	// We do it this way simply to avoid requiring glu.h
	GLdouble fH	= tan( float( _yFieldOfView / 360.0F * M_PI ) ) * _zNear;
	GLdouble fW	= fH * _aspect;

	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();

	glFrustum( -fW, fW, -fH, fH, _zNear, _zFar );

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
}
